package org.loong.crypto.service.core.provider.impl;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

import org.apache.commons.lang3.ArrayUtils;
import org.loong.crypto.common.exception.InvalidKeyException;
import org.loong.crypto.common.exception.InvalidKeyLengthException;
import org.loong.crypto.core.algorithm.HmacAlgorithm;
import org.loong.crypto.service.core.context.CryptoContext;
import org.loong.crypto.service.core.provider.HmacProvider;

/**
 * The base abstract class for Hash Message Authentication Code (HMAC) signers and verifiers.
 * 
 * <p>
 * Supports the following algorithms:
 *
 * <ul>
 * <li>{@link org.loong.crypto.core.algorithm.HmacAlgorithm#HMAC_MD5}
 * <li>{@link org.loong.crypto.core.algorithm.HmacAlgorithm#HMAC_SHA1}
 * <li>{@link org.loong.crypto.core.algorithm.HmacAlgorithm#HMAC_SHA224}
 * <li>{@link org.loong.crypto.core.algorithm.HmacAlgorithm#HMAC_SHA256}
 * <li>{@link org.loong.crypto.core.algorithm.HmacAlgorithm#HMAC_SHA384}
 * <li>{@link org.loong.crypto.core.algorithm.HmacAlgorithm#HMAC_SHA512}
 * <li>{@link org.loong.crypto.core.algorithm.HmacAlgorithm#HMAC_SM3}
 * </ul>
 */
public abstract class BaseHmacProvider implements HmacProvider {

    /**
     * The supported hmac algorithms by the HMAC provider class.
     */
    public static final Set<HmacAlgorithm> SUPPORTED_ALGORITHMS;

    /**
     * The cryptoContext.
     */
    private CryptoContext cryptoContext;
    
    static {
        Set<HmacAlgorithm> algs = new LinkedHashSet<>();
        algs.add(HmacAlgorithm.HMAC_MD5);
        algs.add(HmacAlgorithm.HMAC_SHA1);
        algs.add(HmacAlgorithm.HMAC_SHA224);
        algs.add(HmacAlgorithm.HMAC_SHA256);
        algs.add(HmacAlgorithm.HMAC_SHA384);
        algs.add(HmacAlgorithm.HMAC_SHA512);
        algs.add(HmacAlgorithm.HMAC_SM3);
        SUPPORTED_ALGORITHMS = Collections.unmodifiableSet(algs);
    }

    @Override
    public Set<HmacAlgorithm> supportedHmacAlgorithms() {
        return SUPPORTED_ALGORITHMS;
    }

    /**
     * The secret.
     */
    private final byte[] secret;

    /**
     * Creates a new Hash Message Authentication (HMAC) provider.
     *
     * @param secret the secret. Must be at least 128 bits long and not {@code null}.
     * @throws InvalidKeyException if the secret is null
     * @throws InvalidKeyLengthException if the secret length is shorter than the minimum 128-bit requirement.
     */
    protected BaseHmacProvider(final byte[] secret) throws InvalidKeyException, InvalidKeyLengthException {
        if (ArrayUtils.isEmpty(secret)) {
            throw new InvalidKeyException("The secret cannot be null");
        }
        
        if (secret.length < 128 / 8) {
            throw new InvalidKeyLengthException("The secret length must be at least 128 bits");
        }

        this.secret = secret;
    }

    /**
     * Gets the secret bytes.
     *
     * @return the secret bytes.
     */
    public byte[] getSecret() {
        return secret;
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public <T extends CryptoContext> T getCryptoContext() {
        return (T) cryptoContext;
    }

    @Override
    public void setCryptoContext(CryptoContext cryptoContext) {
        this.cryptoContext = cryptoContext;
    }
}
